package com.cuit.spring;

import com.cuit.annotation.HystrixComman;
import com.cuit.annotation.RpcReference;
import com.cuit.annotation.RpcService;
import com.cuit.config.RpcServiceConfig;
import com.cuit.extension.ExtensionLoader;
import com.cuit.factory.SingleFactory;
import com.cuit.mock.MockServiceManager;
import com.cuit.provider.CurrentStateTimer;
import com.cuit.provider.ServiceProvider;
import com.cuit.provider.impl.ZkServiceProviderImpl;
import com.cuit.proxy.RpcClientProxy;
import com.cuit.remoting.transport.RpcRequestTransport;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.lang.reflect.Field;

/**
 * 查看类是否有我们自定义的注解，有的话做特殊操作
 * @PROJECT_NAME: rpc
 * @SCHOOL:CUIT
 * @USER: Liquid
 * @DATE: 2022/3/8 14:12
 */
@Slf4j
@Component
public class SpringBeanPostProcessor implements BeanPostProcessor , ApplicationContextAware {

    private final ServiceProvider serviceProvider;
    private final RpcRequestTransport rpcClient;
    private final CurrentStateTimer currentStateTimer;
    private final MockServiceManager mockServiceManager;
    private ApplicationContext applicationContext;

    public SpringBeanPostProcessor() {
        this.serviceProvider = SingleFactory.getSingle(ZkServiceProviderImpl.class);
        this.currentStateTimer = SingleFactory.getSingle(CurrentStateTimer.class);
        this.mockServiceManager = SingleFactory.getSingle(MockServiceManager.class);
        this.rpcClient = ExtensionLoader.getExtensionLoader(RpcRequestTransport.class).getExtension("netty");
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    /**bean前置处理器
     * 注册RPCservice注解的信息到zookeeper服务器
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean.getClass().isAnnotationPresent(RpcService.class)){
            log.info("该[{}]有自定义的{}注解",bean.getClass().getName(),RpcService.class.getCanonicalName());
            RpcService annotation = bean.getClass().getAnnotation(RpcService.class);
            RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder().group(annotation.group())
                    .service(bean)
                    .version(annotation.version())
                    .degradeServicePath(annotation.degradeServicePath())
//                    .enableSupportService(annotation.isSupportDegradeSerivce())
                    .vip(annotation.vip())
                    .build();

            currentStateTimer.put(rpcServiceConfig);
            serviceProvider.publishService(rpcServiceConfig);
        }

        return bean;
    }

    /**
     * bean后置处理器
     * 为@RpcReference的字段注入动态代理过的属性，可以进行网络调用
     * @param bean
     * @param beanName
     * @return
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        Class<?> targetClass = bean.getClass();
        Field[] declaredFields = targetClass.getDeclaredFields();
        for (Field field : declaredFields) {
            RpcReference annotation = field.getAnnotation(RpcReference.class);
            if(annotation != null){
                RpcServiceConfig rpcServiceConfig = RpcServiceConfig.builder().version(annotation.version())
                        .group(annotation.group()).maxCount(annotation.maxCount()).timeout(annotation.timeout()).service(bean)
                                .build();
                //设置mock到mockmananger
                String fallBackService = annotation.fallBackService();
                if (StringUtils.hasLength(fallBackService))
                {
                    Class<?> declaringClass = field.getType();
                    String rpcServiceName = declaringClass.getCanonicalName() + annotation.group() + annotation.version();
                    mockServiceManager.putMockService(rpcServiceName, getMockServiceBean(fallBackService));
                }

                RpcClientProxy rpcClientProxy = new RpcClientProxy(rpcClient,rpcServiceConfig);
                Object proxy = rpcClientProxy.getProxy(field.getType());
                field.setAccessible(true);
                try {
                    field.set(bean,proxy);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
        return bean;
    }

    private Object getMockServiceBean(String name){
        if (applicationContext != null){
            try{
                return applicationContext.getBean(name);
            }catch (Exception e){
                e.printStackTrace();
                throw new IllegalStateException("客户端的降级服务需要注册到spring容器");
            }
        }
        throw new IllegalStateException("spring初始化失败");
    }

}
